using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Xna.Framework;

namespace EasyPak
{
    public class PakFile : IEnumerable<PakEntry>
    {
        private bool _debug = false;
        private Dictionary<string, PakEntry> _entries = null;

        private string _pakFileName;
        public string PakFileName
        {
            get { return _pakFileName; }
        }

        private byte[] _allFilesData;

        private PakFile() 
        {
        }


        #region For Writing Pak Files

        public PakFile(string newPakFileName)
        {
            // create a new Pakfile
            _pakFileName = Path.GetFullPath(newPakFileName);
            if (File.Exists(_pakFileName))
                throw new System.Exception(String.Format("The specified content pak file ({0}) already exists.", _pakFileName));

            _entries = new Dictionary<string, PakEntry>();
        }

        
        public void AddFile(string fileName)
        {
            AddFile(fileName, false);
        }

        public void AddFile(string fileName, bool WantVerbose)
        {
            if (!File.Exists(fileName))
                throw new Exception(String.Format("The specified content file ({0}) does not exist!", fileName));

            PakEntry pe = PakEntry.Create(fileName.ToLower().Replace('/', '\\'));
            if (WantVerbose) Console.WriteLine("adding {0}...", fileName);

            _entries.Add(pe.FileName, pe);
        }

        public void Save()
        {
            Stream writeStream = File.OpenWrite(this._pakFileName);
            foreach (PakEntry pe in _entries.Values)
            {
                pe.Write(writeStream);
            }
            writeStream.Flush();
            writeStream.Close();
            writeStream.Dispose();
        }

        
        #endregion

        #region For Reading pak Files

        /// <summary>
        /// This will throw if the pakfile does not exist. 
        /// </summary>
        public static PakFile Read(string pakfilename)
        {
            return Read(pakfilename, false);
        }

        /// <summary>
        /// This will throw if the pakfile does not exist. 
        /// </summary>
        public static PakFile Read(string pakfilename, bool turnOnDebug)
        {
            PakFile pf = new PakFile();
            pf.readAllEntries(pakfilename, turnOnDebug);
            return pf;
        }

        static int READ_CHUNK_SIZE = 32768;
        internal void readAllEntries(string pakfilename, bool turnOnDebug)
        {
            _debug = turnOnDebug;
            _pakFileName = pakfilename;
            _entries = new Dictionary<string, PakEntry>();

            // First copy all file data to memory
            Stream readstream = TitleContainer.OpenStream(_pakFileName);
            _allFilesData = new byte[readstream.Length];
            int readPos = 0;
            while (readPos < _allFilesData.Length)
            {
                int bytesRead = readstream.Read(_allFilesData, readPos, Math.Min(_allFilesData.Length - readPos, READ_CHUNK_SIZE));
                readPos += bytesRead;
            }
            readstream.Close();

            // Now open a memory stream for reading through the PakFile data
            readstream = new MemoryStream(_allFilesData, false);

            PakEntry e;
            int entryId = 0;
            List<PakEntry> entries = new List<PakEntry>();
            while ((e = PakEntry.Read(readstream, _allFilesData, _debug)) != null)
            {
                if (_debug) System.Console.WriteLine("  Read new PakEntry: {0}", e.FileName);
                _entries.Add(e.FileName.ToLower().Replace('/', '\\'), e);
                
                entries.Add(e);
                entryId++;
            }

            readstream.Close();
            readstream.Dispose();
        }
        #endregion

        public PakEntry this[String filename]
        {
            get
            {
                PakEntry pe = null;
                if (_entries.TryGetValue(
                        filename.ToLower().Replace('/', '\\'), 
                        out pe))
                {
                    return pe;
                }
                return null;
            }
        }

        public IEnumerator<PakEntry> GetEnumerator()
        {
            return _entries.Values.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return _entries.Values.GetEnumerator();
        }
    }
}